import json
import logging

from django.conf import settings
from django.utils.deprecation import MiddlewareMixin
from system.models import SysOperLog
from .request_utils import (
    get_request_user, get_request_ip, get_request_data, get_request_path,
    get_os, get_browser, get_verbose_name, get_ip_analysis
)

logger = logging.getLogger('django')

class ApiLoggingMiddleware(MiddlewareMixin):
    """
    用于记录API访问日志中间件
    """

    def __init__(self, get_response=None):
        super().__init__(get_response)
        self.enable = getattr(settings, 'API_LOG_ENABLE', False)
        self.methods = getattr(settings, 'API_LOG_METHODS', set())
        self.operation_log_id = None

    @classmethod
    def __handle_request(cls, request):
        request.request_ip = get_request_ip(request)
        request.request_data = get_request_data(request)
        request.request_path = get_request_path(request)

    def _business_type_map(self, request):
        method_to_type = {'POST': 1, 'PUT': 2, 'DELETE': 3}
        return method_to_type.get(request.method, 0)

    def _get_login_location(self, ip):
        if ip != 'unknown':
            analysis_data = get_ip_analysis(ip)
            if analysis_data['country'] == '保留':
                return '内网IP'
            return f"{analysis_data['country']} {analysis_data['province']} {analysis_data['city']}"
        return 'unknown'

    def __handle_response(self, request, response):
        """
        处理请求响应的中间件方法。
        该方法主要用于捕获请求和响应，对其中的数据进行处理，并记录操作日志。

        :param request: 请求对象，包含请求的相关信息
        :param response: 响应对象，包含响应的相关信息
        """
        # 尝试获取请求体中的数据，并检查是否包含密码字段
        body = getattr(request, 'request_data', {})
        if isinstance(body, dict) and 'password' in body:
            # 如果包含密码字段，将其替换为星号以保护隐私
            body['password'] = '*' * len(body['password'])

        # 尝试从响应对象中获取数据，如果数据为空，则尝试从响应内容中解析JSON数据

        response_data = getattr(response, 'data', {})
        if not response_data and response.content:
            try:
                content = json.loads(response.content.decode())
                response_data = content if isinstance(content, dict) else {}
            except json.JSONDecodeError:
                response_data = {}

        # 构建操作日志的信息
        info = {
            'business_type': self._business_type_map(request),
            'method': request.method,
            'request_method': request.method,
            'oper_name': get_request_user(request),
            'oper_url': request.request_path,
            'oper_ip': getattr(request, 'request_ip', 'unknown'),
            'oper_location': self._get_login_location(getattr(request, 'request_ip', 'unknown')),
            'oper_param': body,
            'json_result': response_data if response_data else '{}',  # 确保 json_result 不为 NULL
            'status': bool(response_data),
            'error_msg': '',
        }

        # 记录操作日志
        logger.info('中间件请求参数：%s' % info)
        operation_log, created = SysOperLog.objects.update_or_create(defaults=info, oper_id=self.operation_log_id)
        if not operation_log.request_method and settings.API_MODEL_MAP.get(request.request_path):
            # 如果操作日志中请求方法为空，并且请求路径在API模型映射中存在，则更新请求方法
            operation_log.request_method = settings.API_MODEL_MAP[request.request_path]
            operation_log.save()


    def process_view(self, request, view_func, view_args, view_kwargs):
        """
        处理视图函数执行前的逻辑，旨在根据条件记录系统操作日志。
        在服务启用且请求方法匹配配置时，检查视图函数以提取查询集信息，
        根据此信息创建操作日志条目。增强了对`view_func`可能为None情况的处理。

        :param request: 当前HTTP请求对象，包含请求方法等信息。
        :param view_func: 即将执行的视图函数或其引用。
        :param view_args: 传递给视图函数的位置参数列表。
        :param view_kwargs: 传递给视图函数的关键字参数字典。
        """

        # 确保日志记录功能已启用且请求方法符合记录要求
        if self.enable and (self.methods == 'ALL' or request.method in self.methods):

            try:
                # 安全地尝试访问视图函数的queryset属性或调用get_queryset方法
                queryset = (getattr(view_func, 'queryset', None)
                           or (view_func.get_queryset() if hasattr(view_func, 'get_queryset') else None))

                # 若成功获取到查询集，则继续创建并保存操作日志
                if queryset is not None:
                    verbose_name = get_verbose_name(queryset=queryset)
                    log = SysOperLog(title=verbose_name)
                    log.save()
                    self.operation_log_id = log.oper_id
            except AttributeError as ae:
                # 处理视图函数缺少预期属性或方法的情况
                logger.warning(f"AttributeError in process_view: {ae}, possibly due to missing 'queryset' or 'get_queryset'.")
            except Exception as e:
                # 记录其他异常，确保不影响主流程
                logger.error(f"Unexpected error in process_view: {e}")



    def process_request(self, request):
        self.__handle_request(request)
        user = get_request_user(request)
        setattr(request, 'user', user)

    def process_response(self, request, response):
        if self.enable and (self.methods == 'ALL' or request.method in self.methods):
            self.__handle_response(request, response)
        return response
